#include "TService.h"
#include <iostream>

TService * Service = 0;

#define REG_CONFIG   "SYSTEM\\CurrentControlSet\\Services\\%s\\ServiceConfig"
#define REG_EVENTLOG "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System"

void WINAPI TService::ServiceMain (DWORD argc, char* argv[]) 
{
	
	Service->m_StatusHandle = RegisterServiceCtrlHandler(Service->GetName(), ServiceHandler);
	if (Service->succeeded (Service->m_StatusHandle != NULL)) 
	{
		Service->SetStatus (SERVICE_START_PENDING);

		if (Service->Init()) 
		{
			Service->SetStatus (SERVICE_RUNNING);
			Service->ServiceProc();
			Service->SetStatus (SERVICE_STOP_PENDING);
			Service->Cleanup();
			Service->SetStatus (SERVICE_STOPPED);
		}
		else 
			Service->SetStatus (SERVICE_STOPPED);
	}
	else 
		Service->SetStatus (SERVICE_STOPPED);
}

void WINAPI TService::ServiceHandler (DWORD control) 
{
	switch (control) 
	{
		case SERVICE_CONTROL_INTERROGATE:
			Service->SetStatus (Service->m_StatusCode);
			break;

		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			Service->SetStatus (SERVICE_STOP_PENDING);
			Service->Terminate(); ///m_Terminated = true;
			break;
	}
}

BOOL WINAPI TService::ConsoleHandler (DWORD dwCtrlType)
{
	if (dwCtrlType == CTRL_LOGOFF_EVENT)
		Service->LogoffEvent ();
	else 
		Service->ShutdownEvent ();
	return TRUE;
}

TService::TService() : m_StatusHandle(NULL),m_StatusCode(SERVICE_STOPPED),m_Terminated(false) 
{
	Service = this;
	ZeroMemory (m_ErrorString,sizeof(m_ErrorString));
	succeeded(SetConsoleCtrlHandler (ConsoleHandler,TRUE));
}


TService::~TService() 
{
	if (m_EventLog)
		DeregisterEventSource (m_EventLog);
}

bool TService::shopen (tSvcHandle &hService) 
{
	bool 			success = false;
	tSvcHandle	hSCM;

	if ((hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS)) != NULL)	
	{
		hService = OpenService(hSCM,GetName(),SERVICE_ALL_ACCESS);
		success = succeeded (hService != NULL);
		CloseServiceHandle (hSCM);
	}
	else 
		succeeded (false);

	return success;
}

void TService::shclose (tSvcHandle hService) 
{
	CloseServiceHandle (hService);
}

void TService::SetStatus (DWORD status) 
{
	m_StatusCode  = status;

	SERVICE_STATUS ss;
	ss.dwServiceType              = SERVICE_WIN32_OWN_PROCESS;
	ss.dwCurrentState             = m_StatusCode;
	ss.dwControlsAccepted	      = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
	ss.dwWin32ExitCode            = NOERROR;
	ss.dwServiceSpecificExitCode  = NOERROR;
	ss.dwCheckPoint               = 0;
	ss.dwWaitHint                 = 3000;

	if (! succeeded (SetServiceStatus (m_StatusHandle,&ss)))
		LogEvent (m_ErrorString,evWarning);
}

bool TService::succeeded (BOOL success) 
{
	DWORD SysError = success ? ERROR_SUCCESS : GetLastError();

	FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,NULL,SysError,
										MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
										m_ErrorString,sizeof(m_ErrorString),NULL);
	return SysError == ERROR_SUCCESS;
}

// -----------------------------------------------------------------------------

bool TService::Execute () 
{
	m_EventLog = RegisterEventSource (NULL,GetName());

	SERVICE_TABLE_ENTRY entries[2];
	entries[0].lpServiceName = (char*) GetName();
	entries[0].lpServiceProc = ServiceMain;
	entries[1].lpServiceName = NULL;
	entries[1].lpServiceProc = NULL;

	bool  success = succeeded (StartServiceCtrlDispatcher(entries));
	if (! success)
		LogEvent (m_ErrorString,evError);

	return success;
}

bool TService::ConsoleMode (void) 
{
	m_EventLog = RegisterEventSource(NULL,GetName());

	bool success = false;
	if (Init())	
	{
		ServiceProc();
		Cleanup ();
		success = true;
	}
	return success;
}

bool TService::Start() 
{
	bool         success = false;
	tSvcHandle   hService;

	if (shopen (hService)) 
	{
		success = succeeded (StartService (hService,0,NULL));
		shclose (hService);
	}
	return success;
}

// ----------------------------

bool TService::Stop() 
{
	bool         success = false;
	tSvcHandle   hService;

	if (shopen (hService)) 
	{
		SERVICE_STATUS   state;
		success = succeeded (ControlService (hService,SERVICE_CONTROL_STOP,&state));
		shclose (hService);
	}
	return success;
}

bool TService::Remove() 
{
	Stop();

	bool         success = false;
	tSvcHandle   hService;

	if (shopen (hService)) 
	{
		success = succeeded (DeleteService (hService));
		shclose (hService);

		HKEY hKey;
		if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,REG_EVENTLOG,0,KEY_ALL_ACCESS,&hKey) == ERROR_SUCCESS) 
		{
			RegDeleteKey (hKey,GetName());
			RegCloseKey (hKey);
		}
	}
	return success;
}

bool TService::Install() 
{
	bool        success = false;
	SC_HANDLE   hService,hSCManager;
	char        imagePath[MAX_PATH];

	GetModuleFileName(NULL,imagePath,MAX_PATH);
	if (succeeded ((hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS))!= NULL)) 
	{
		if (succeeded ((hService = ::CreateService (hSCManager,GetName(),GetDisplayName(),SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,imagePath,NULL,NULL,NULL,NULL,NULL)) != NULL)) 
		{
			CloseServiceHandle (hService);
			success = true;
		}
		CloseServiceHandle (hSCManager);
	}

	if (success) 
	{
		UINT f = EVENTLOG_ERROR_TYPE|EVENTLOG_WARNING_TYPE|EVENTLOG_INFORMATION_TYPE;

		char   szKey [MAX_PATH];
		wsprintf (szKey,"%s\\%s",REG_EVENTLOG,GetName());

		HKEY hKey;
		if (RegCreateKey (HKEY_LOCAL_MACHINE,szKey,&hKey) == ERROR_SUCCESS) 
		{
			char   mod[MAX_PATH];
			DWORD  len = GetModuleFileName (NULL,mod,MAX_PATH);

			RegSetValueEx(hKey,"TypesSupported",0,REG_DWORD,(BYTE*) &f,sizeof(DWORD));
			RegSetValueEx(hKey,"EventMessageFile",0,REG_SZ,(BYTE*) mod,len+1);
			RegCloseKey (hKey);
		}
	}
	return success;
}

bool TService::Help (DWORD context) 
{
	char file [MAX_PATH +6],*p;
	GetModuleFileName (NULL,file,MAX_PATH);
	if ((p = strrchr (file,'.')) != NULL)
	{
		strcpy_s ( p, 5, ".hlp" );
	}
	return WinHelp (NULL,file,context ? HELP_CONTEXT : HELP_CONTENTS,context) != 0;
}

void TService::PrintLastError (const char *cap) 
{
	if ( cap )
		std::cout << cap << ": ";
	
	char oem[sizeof(m_ErrorString)];
	ZeroMemory (oem,sizeof(oem));
	CharToOemBuff (m_ErrorString,oem,(DWORD) strlen(m_ErrorString));	
	std::cout << oem << std::endl;
}

void TService::Print( const std::string & cap )
{
	std::cout << cap << std::endl;
	
}

// ----------------------------

void TService::LogEvent (const std::string & str,evLogType type,WORD Category) 
{
	if (m_EventLog != NULL) 
	{
		const char* msgs[1];
		msgs[0] = str.c_str();
		ReportEvent (m_EventLog,(WORD) type,Category,0,NULL,1,0,msgs,NULL);
	}
}

void TService::Terminate ()
{
	m_Terminated = true;
}


extern "C" void LogEvent (const char * str,int type,WORD Category) 
{
	if ( Service )
	{
		Service->LogEvent(str, (TService::evLogType)type, Category);
	}
}